
#include <fstream>
#include <iterator>
#include "mesh.h"

Mesh::Mesh() {
   
   _nvertices=_nfaces=0;
}

Mesh::Mesh(const char* filename) {
   
   _nvertices=_nfaces=0;
   loadShape(filename);
}

Mesh::~Mesh() {
   
}

void Mesh::loadShape(const char *filename) {
   
   cout<<"Loading... "<<endl;
   
   std::ifstream finput(filename);
   assert(finput);
   
   unsigned int nv, nt, ne;
   finput.ignore(numeric_limits<int>::max(),'\n');
   finput>>nv>>nt>>ne;
   
   _nvertices = nv;
   _nfaces    = nt;
   cout<<"..."<<nv<<" "<<nt<<" "<<ne<<endl;
   
   positions.resize(nv);
   normals.resize(nv);
   facespervertex.resize(nv);
   faces.resize(nt*3);
   facenormals.resize(nt);
   
   Number x, y, z;
   for (unsigned int i=0; i<nv; i++) {
      finput>>x>>y>>z;
      positions[i] = Point3(x,y,z);
   }
   
   int n, v0, v1, v2;
   for (unsigned int i=0; i<nt; i++) {
      finput>>n;
      if (n==3) {
         finput>>v0>>v1>>v2;
         faces[i*3]=v0; faces[i*3+1]=v1; faces[i*3+2]=v2;
         facespervertex[v0].insert(i);
         facespervertex[v1].insert(i);
         facespervertex[v2].insert(i);
      }
      else {
         cout<<"this version supports triangles only"<<endl;
         assert(true);
      }
   }
   
   finput.close();
   auxiliarData();
   cout<<"Loading... done!"<<endl;
}

void Mesh::saveShapeOFF(const char *filename) {
   
}

Mesh* Mesh::load_off(const char *filename) {
   
   std::fstream off_file(filename, std::ios_base::in);
   if (off_file) {
      Mesh *p = new Mesh(filename);
      return p;
   }
   return 0; 
}

void Mesh::genFacesNormals() {
   
   for (uint i=0; i<facenormals.size(); ++i) {
      const Point3 &p0 = positions[faces[i*3  ]];
      const Point3 &p1 = positions[faces[i*3+1]];
      const Point3 &p2 = positions[faces[i*3+2]];
      facenormals[i] = cross_product(Vector3(p0,p1), Vector3(p0,p2));
   }
}

void Mesh::genVerticesNormals() {
   
   for (uint i=0; i<facespervertex.size(); ++i) {
      const set<int> &incidentfaces = facespervertex[i];
      set<int>::const_iterator it = incidentfaces.begin();
      Vector3 s(0,0,0); int c=0;
      while (it != incidentfaces.end()) {
         const int &f = *it++;
         s = s + facenormals[f];
         ++c;
      }
      Number l = s*s;
      if (l > 0) normals[i] = s/sqrt(l);
   }
}

void Mesh::auxiliarData() {
   
   cout<<endl<<"Loading Done!"<<endl;
   cout<<"Vertices Number: "<<_nvertices<<endl;
   cout<<"Triangles Number: "<<_nfaces<<endl;
   
   /// Compute the normal vector of each face
   genFacesNormals();
   
   /// Compute the normal vector of each vertex
   genVerticesNormals();
   
   /// Computes the limits of the model
   limits();
   
   /// Centers and scales the model
   centerAndScale();
}

void Mesh::limits() {
   
   vector<Point3>::iterator vit = positions.begin();
   const Point3 &p = *vit;
   for (int i = 0; i < 3; ++i) { mincoo[i] = maxcoo [i] = p[i]; }
   
   while (vit != positions.end()) {
      const Point3 &p = *vit++;
      for (int i = 0; i < 3; ++i) {
         if (p[i] < mincoo[i])      mincoo[i] = p[i];
         else if (p[i] > maxcoo[i]) maxcoo[i] = p[i];
      }
   }
   
   /// building the minimum bounding cube 
   Point3 WorldpMin(mincoo[0],mincoo[1],mincoo[2]);
   Point3 WorldpMax(maxcoo[0],maxcoo[1],maxcoo[2]);
   
   cout<<"pmin: "<<mincoo[0]<<" "<<mincoo[1]<<" "<<mincoo[2]<<endl;
   cout<<"pmax: "<<maxcoo[0]<<" "<<maxcoo[1]<<" "<<maxcoo[2]<<endl;
   
   Vector3 size(WorldpMax[0]-WorldpMin[0],WorldpMax[1]-WorldpMin[1],WorldpMax[2]-WorldpMin[2]);
   size = size*0.5;
   Point3 center = WorldpMin + size;
   double radius = sqrt(size*size); // vector lenght
   size = Vector3(radius, radius, radius);
   WorldpMin = center - size;
   WorldpMax = center + size;
   
   mincoo[0]=WorldpMin[0]; mincoo[1]=WorldpMin[1]; mincoo[2]=WorldpMin[2];
   maxcoo[0]=WorldpMax[0]; maxcoo[1]=WorldpMax[1]; maxcoo[2]=WorldpMax[2];
}

void Mesh::centerAndScale(Number ScaleFactor) {
   
   assert(ScaleFactor > 0);
   
   Number dimv[]={maxcoo[0]-mincoo[0], maxcoo[1]-mincoo[1], maxcoo[2]-mincoo[2]};
   assert(dimv[0]>0 and dimv[1]>0 and dimv[2]>0);
   
   // Determine the scale factor
   Number Scale;
   if (dimv[0] >= dimv[1] && dimv[0] >= dimv[2]) Scale = ScaleFactor/dimv[0];
   else if (dimv[1] >= dimv[0] && dimv[1] >= dimv[2]) Scale = ScaleFactor/dimv[1];
   else Scale = ScaleFactor/dimv[2];
   
   // Determine the translation vector
   Number transv[]={mincoo[0]+maxcoo[0], mincoo[1]+maxcoo[1], mincoo[2]+maxcoo[2]};
   transv[0] *= 0.5f;
   transv[1] *= 0.5f;
   transv[2] *= 0.5f;
   
   vector<Point3>::iterator vit = positions.begin();
   while (vit != positions.end()) {
      Point3 &p = *vit++;
      p = Point3(p[0]-transv[0], p[1]-transv[1], p[2]-transv[2]);
      p = Point3(p[0]*Scale, p[1]*Scale, p[2]*Scale);
   }
}
